package Planificador;

import java.util.ArrayList;

public class SistemaOperativo {
	protected Procesador procesador;

	// todos los procesos del ejercicio
	protected ArrayList<Proceso> procesos = new ArrayList<Proceso>();
	protected ArrayList<Proceso> procesosTerminados = new ArrayList<Proceso>();

	protected Cola nuevos = new Cola(new AlgoritmoFIFO());
	protected Cola suspendidos = new Cola(new AlgoritmoFIFO());
	protected int multiprogramacion = 0;
	protected static int nQuantum = 0;

	protected ArrayList<DispositivoExterno> dispositivosExternos = new ArrayList<DispositivoExterno>();

	protected ArrayList<String> logs = new ArrayList<String>();

	public SistemaOperativo() {
		SistemaOperativo.nQuantum = 0;
		this.procesador = new Procesador();
		Registro.reset();
	}

	public void resetear() {
		Registro.reset();
		this.procesador.reset();
		// todos los procesos del ejercicio
		this.procesos.clear();
		this.procesosTerminados.clear();

		this.nuevos.reset();
		this.suspendidos.reset();
		this.multiprogramacion = 0;
		SistemaOperativo.nQuantum = 0;
	}

	public DispositivoExterno agregarDispositivoExterno(String nombre) {
		DispositivoExterno d = new DispositivoExterno(nombre);
		this.dispositivosExternos.add(d);

		return d;
	}

	public SistemaOperativo agregarProceso(Proceso p) {
		p.inicializar();
		this.procesos.add(p);
		return this;
	}

	public void eliminarDispositivoExterno(DispositivoExterno d) {
		this.dispositivosExternos.remove(d);
	}

	public void eliminarDispositivos() {
		this.dispositivosExternos.clear();
	}

	public void entregarQuantum() {

		this.crearProcesosNuevos();

		this.recibirQuantum(procesador);

		this.actualizarColas();

		for (DispositivoExterno dispositivo : this.dispositivosExternos) {
			this.recibirQuantum(dispositivo);
		}

		SistemaOperativo.nQuantum++;
		if (this.procesador.hayQueDesalojarPorTimer()) {
			this.desalojarProceso();
		}

	}

	public Procesador getProcesador() {
		return this.procesador;
	}

	private void actualizarColas() {
		// TODO this.tratarMultiprogramacion();
		Proceso p = this.nuevos.siguienteProceso();

		if (p != null) {
			this.procesador.agregarProceso(p);
			Registro.moviento(p, "nuevos", "listos");
		}
	}

	private void crearProcesosNuevos() {
		for (Proceso p : this.procesos) {
			if (p.getTiempoEntrada() == SistemaOperativo.nQuantum) {
				Registro.entradaProceso(SistemaOperativo.nQuantum, p);
				this.nuevos.addProceso(p);
				this.actualizarColas();
			}
		}

		if (this.procesador.hayQueDesalojar()) {
			this.desalojarProceso();
		}
	}

	private void desalojarProceso() {
		Proceso p = this.procesador.sacarProcesoEnEjecucion();
		Registro.moviento(p, "procesador", "listos");
		this.procesador.agregarProceso(p);
	}

	private void recibirQuantum(DispositivoExterno dispositivo) {
		try {
			dispositivo.recibirQuantum();

		} catch (ConsumioTodaLaRafagaEntradaSalidaException e) {
			Proceso procesoEnEjecucion = dispositivo.sacarProcesoEnEjecucion();

			Registro.movientoHack(procesoEnEjecucion, dispositivo.toString(),
					"listos");

			procesoEnEjecucion.moverseAsigInstruccion();

		} catch (FinDeProcesoException e) {
			// nunca se dan estas excepciones
		} catch (ConsumioTodaLaRafagaCpuException e) {
			// nunca se dan estas excepciones
		}

	}

	private void recibirQuantum(Procesador dispositivo) {
		try {
			this.procesador.recibirQuantum();

		} catch (ConsumioTodaLaRafagaCpuException e) {
			// TODO REVISA ACA ESTA EL PROBLEMA
			Proceso procesoEnEjecucion = this.procesador
					.sacarProcesoEnEjecucion();
			procesoEnEjecucion.moverseAsigInstruccion();
			this.recibirQuantum(this.procesador);
		} catch (FinDeProcesoException e) {
			Proceso p = dispositivo.sacarProcesoEnEjecucion();
			Registro.salidaProceso(SistemaOperativo.nQuantum, p);
			this.procesosTerminados.add(p);
			this.recibirQuantum(this.procesador);
		} catch (ConsumioTodaLaRafagaEntradaSalidaException e) {
		}

	}

	private void tratarMultiprogramacion() {
		if (!this.multiprogramacionEnLimite()) {
			if (this.hayProcesosSuspendidos()) {
				// TODO mas adelante soportar multiprogramacion
			} else {

				this.procesador.agregarProceso(this.nuevos.siguienteProceso());
			}
		}

	}

	private boolean hayProcesosSuspendidos() {
		return (this.multiprogramacion > 0) && (this.suspendidos.size() > 0);
	}

	private boolean multiprogramacionEnLimite() {
		// TODO Auto-generated method stub
		return false;
	}

	public static int getQuantum() {
		return SistemaOperativo.nQuantum;
	}

	public ArrayList<Proceso> getProcesos() {
		return this.procesos;
	}

	public ArrayList<Proceso> getTerminados() {
		return this.procesosTerminados;
	}

	public void correrEjercicio() {
		while (this.procesos.size() != this.procesosTerminados.size()) {
			this.entregarQuantum();
		}
	}

	public void setAlgoritmo(Algoritmo algoritmo) {
		this.procesador.setAlgoritmo(algoritmo);

	}

}
